﻿/**********************************************************/
/* Bewegen klasse */
/* 13-10-2010 - Groep 6: Scheidsrechter(Lars Mohr 0629693, Roy Mengelers 0705020, Roy Lieben 0847186, Timothy Janssen 0951986, Lars Brand 0901164 - */
/*	
/* Omschrijving:
/* De klasse regelt alles met betrekking tot het bewegen van de speler.*/
/**********************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using Roborally.CommonData.Enums;
using Roborally.CommonData.GlobaleTypes;

namespace Roborally.Scheidsrechter
{
    internal static class Bewegen
    {
        /// <summary>
        /// Methode om de speler naar West te draaien.
        /// </summary>
        /// <param name="SpelerGegevens">Huidige speler</param>
        public static void DraaiSpelerWest(SpelerGegevens SpelerGegevens)
        {
            switch (SpelerGegevens.Richting)
            {
                case Richting.Noord:
                    SpelerGegevens.Richting = Richting.West;
                    break;
                case Richting.Zuid:
                    SpelerGegevens.Richting = Richting.Oost;
                    break;
                case Richting.Oost:
                    SpelerGegevens.Richting = Richting.Noord;
                    break;
                case Richting.West:
                    SpelerGegevens.Richting = Richting.Zuid;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Methode om de speler naar Oost te draaien.
        /// </summary>
        /// <param name="SpelerGegevens">Huidige speler</param>
        public static void DraaiSpelerOost(SpelerGegevens GegevensEenSpeler)
        {
            switch (GegevensEenSpeler.Richting)
            {
                case Richting.Noord:
                    GegevensEenSpeler.Richting = Richting.Oost;
                    break;
                case Richting.Zuid:
                    GegevensEenSpeler.Richting = Richting.West;
                    break;
                case Richting.Oost:
                    GegevensEenSpeler.Richting = Richting.Zuid;
                    break;
                case Richting.West:
                    GegevensEenSpeler.Richting = Richting.Noord;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
        
        /// <summary>
        /// Speler bewegen op het bord, rekening houdende met:
        /// - Lopen in een valkuil.
        /// - Van het bord lopen.
        /// - Tegen een muur lopen.
        /// - Duwen van andere spelers.
        /// </summary>
        /// <param name="GegevensSpelers">Lijst van alle tegenstanders.</param>
        /// <param name="GegevensEenSpeler">Huidige speler waar de verplaatsing voor dient.</param>
        /// <param name="LoopRichting">Richting waarin de speler zich verplaatst.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns>Bewegen uitgevoerd?</returns>
        public static bool ZetStap(List<SpelerGegevens> GegevensSpelers, SpelerGegevens GegevensEenSpeler, Richting LoopRichting, IModel Bord)
        {
            bool zetStap = false;
            Point nieuwePositie = NieuwePositie(GegevensEenSpeler.Positie, LoopRichting);
            if (LooptInValkuil(GegevensEenSpeler, Bord, nieuwePositie, LoopRichting) || !PositieOpBord(nieuwePositie, Bord))
            {
                zetStap = true;
                GegevensEenSpeler.Positie = new Point(-1, -1);
            }
            else if (ZetMogelijk(GegevensSpelers, GegevensEenSpeler, LoopRichting, Bord))
            {
                zetStap = true;
                for (int i = GegevensSpelers.Count - 1; i >= 0; i--)
                {
                    SpelerGegevens speler = GegevensSpelers[i];
                    if (speler != GegevensEenSpeler && speler.Positie.X == nieuwePositie.X && speler.Positie.Y == nieuwePositie.Y)
                    {
                        GegevensSpelers.Remove(speler);
                        zetStap = ZetStap(GegevensSpelers, speler, LoopRichting, Bord);
                        GegevensSpelers.Add(speler);
                    }
                }
                if (zetStap && PositieOpBord(nieuwePositie, Bord))
                    GegevensEenSpeler.Positie = nieuwePositie;
                else
                    GegevensEenSpeler.Positie = new Point(-1, -1);
            }
            return zetStap;
        }

        /// <summary>
        /// Controleerd of een zet mogelijk is, rekening houdende met:
        /// - Loopt tegen muur.
        /// - Anders spelers die in de weg staan.
        /// </summary>
        /// <param name="GegevensSpelers">Lijst van tegenstanders.</param>
        /// <param name="GegevensEenSpeler">Huidige speler.</param>
        /// <param name="LoopRichting">Looprichting v/d speler.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns>Zet mogelijk?</returns>
        public static bool ZetMogelijk(List<SpelerGegevens> GegevensSpelers, SpelerGegevens GegevensEenSpeler, Richting LoopRichting, IModel Bord)
        {
            bool zetMogelijk = false;
            Point nieuwePositie = NieuwePositie(GegevensEenSpeler.Positie, LoopRichting);
            if (!LooptTegenMuur(GegevensEenSpeler, Bord, nieuwePositie, LoopRichting))
            {
                zetMogelijk = true;
                for (int i = GegevensSpelers.Count - 1; i >= 0 && zetMogelijk; i--)
                {
                    SpelerGegevens speler = GegevensSpelers[i];
                    if (speler != GegevensEenSpeler && speler.Positie.X == nieuwePositie.X && speler.Positie.Y == nieuwePositie.Y)
                    {
                        GegevensSpelers.Remove(speler);
                        zetMogelijk = ZetMogelijk(GegevensSpelers, speler, LoopRichting, Bord);
                        GegevensSpelers.Add(speler);
                    }
                }
            }
            return zetMogelijk;
        }

        /// <summary>
        /// Controle of een speler in een valkuil loopt.
        /// </summary>
        /// <param name="GegevensEenSpeler">Huidige speler.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <param name="NieuwePositie">Nieuwe positie van de speler.</param>
        /// <param name="LoopRichting">Looprichting van de speler.</param>
        /// <returns>Speler loopt in valkuil?</returns>
        public static bool LooptInValkuil(SpelerGegevens GegevensEenSpeler, IModel Bord, Point NieuwePositie, Richting LoopRichting)
        {
            bool looptInValkuil = false;
            if (!LooptTegenMuur(GegevensEenSpeler, Bord, NieuwePositie, LoopRichting) && PositieOpBord(NieuwePositie, Bord))
                looptInValkuil = (Bord.Vakken[NieuwePositie.X, NieuwePositie.Y].KrijgType() == VakType.Valkuil);
            return looptInValkuil;
        }

        /// <summary>
        /// Controle of een speler tegen een muur loopt.
        /// </summary>
        /// <param name="GegevensEenSpeler">Huidige speler.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <param name="NieuwePositie">Nieuwe positie van de speler.</param>
        /// <param name="LoopRichting">Looprichting van de speler.</param>
        /// <returns>Speler loopt tegen muur?</returns>
        public static bool LooptTegenMuur(SpelerGegevens GegevensEenSpeler, IModel Bord, Point NieuwePositie, Richting LoopRichting)
        {
            bool looptTegenMuur = false;
            if (PositieOpBord(NieuwePositie, Bord))
            {
                IVak eigenVak = Bord.Vakken[GegevensEenSpeler.Positie.X, GegevensEenSpeler.Positie.Y];
                IVak volgendeVak = Bord.Vakken[NieuwePositie.X, NieuwePositie.Y];
                bool positieNegatief = (GegevensEenSpeler.Positie.X <= NieuwePositie.X && GegevensEenSpeler.Positie.Y <= NieuwePositie.Y);

                Richting tmpRichting;
                switch (LoopRichting)
                {
                    case Richting.West:
                    case Richting.Noord:
                        if (!positieNegatief)
                            tmpRichting = LoopRichting;
                        else
                            tmpRichting = TegenovergesteldeRichting(LoopRichting);
                        break;
                    default:
                        if (positieNegatief)
                            tmpRichting = LoopRichting;
                        else
                            tmpRichting = TegenovergesteldeRichting(LoopRichting);
                        break;
                }
                if (eigenVak == null)
                {
                    Console.WriteLine("omg hij is null");
                }
                else
                {
                    Console.WriteLine("Natuurlijk is het niet null");
                }
                //Console.WriteLine("Eerste: " + eigenVak.KrijgMuur(tmpRichting).ToString());
                if (volgendeVak == null)
                {
                    Console.WriteLine("ja hij is null");
                }
                else
                {
                    Console.WriteLine("nee hij is het niet");
                }
                Console.WriteLine("Eerste: " + eigenVak.KrijgMuur(tmpRichting).ToString());
                Console.WriteLine("Tweede: " + volgendeVak.KrijgMuur(TegenovergesteldeRichting(tmpRichting)));
                return (eigenVak.KrijgMuur(tmpRichting) ||
                    volgendeVak.KrijgMuur(TegenovergesteldeRichting(tmpRichting)));
            }
            return looptTegenMuur;
        }

        /// <summary>
        /// Controle of de speler zich op het bord bevindt.
        /// </summary>
        /// <param name="nieuwePositie">Nieuwe positie van de speler.</param>
        /// <param name="Bord">Huidige status van het bord.</param>
        /// <returns>Speler op bord?</returns>
        public static bool PositieOpBord(Point nieuwePositie, IModel Bord)
        {
            if (nieuwePositie != null)
                return (nieuwePositie.X >= 0 && nieuwePositie.X < Bord.Vakken.GetLength(0) && nieuwePositie.Y >= 0 && nieuwePositie.Y < Bord.Vakken.GetLength(1));
            else
                return false;
        }

        /// <summary>
        /// Vraagt de nieuwe positie op a.d.h.v. de looprichting v/d speler.
        /// </summary>
        /// <param name="Positie">Huidige positie speler.</param>
        /// <param name="Looprichting">Looprichting van de speler.</param>
        /// <returns>Nieuwe positie v/d speler.</returns>
        public static Point NieuwePositie(Point Positie, Richting Looprichting)
        {
            Point nieuwePositie = new Point(Positie.X, Positie.Y);
            switch (Looprichting)
            {
                case Richting.Noord:
                    nieuwePositie.Y -= 1;
                    break;
                case Richting.Zuid:
                    nieuwePositie.Y += 1;
                    break;
                case Richting.Oost:
                    nieuwePositie.X += 1;
                    break;
                case Richting.West:
                    nieuwePositie.X -= 1;
                    break;
                default:
                    break;
            }
            return nieuwePositie;
        }

        /// <summary>
        /// Vraagt de tegenovergestelde richting op van de speler.
        /// </summary>
        /// <param name="HudigeRichting">Huidige richting v/d speler.</param>
        /// <returns>Nieuwe richting v/d speler.</returns>
        public static Richting TegenovergesteldeRichting(Richting HudigeRichting)
        {
            Richting nieuweRichting = HudigeRichting;
            switch (HudigeRichting)
            {
                case Richting.Noord:
                    nieuweRichting = Richting.Zuid;
                    break;
                case Richting.West:
                    nieuweRichting = Richting.Oost;
                    break;
                case Richting.Zuid:
                    nieuweRichting = Richting.Noord;
                    break;
                case Richting.Oost:
                    nieuweRichting = Richting.West;
                    break;
                default:
                    break;
            }
            return nieuweRichting;
        }
    }
}
